package com.physics.mps.module.compute;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.IdUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.physics.mps.common.config.constant.Constants;
import com.physics.mps.common.config.properties.ApiProperties;
import com.physics.mps.common.exception.BusinessException;
import com.physics.mps.common.util.AssertUtil;
import com.physics.mps.common.util.LoginUtil;
import com.physics.mps.common.util.MinioUtil;
import com.physics.mps.common.util.OkHttpUtil;
import com.physics.mps.entity.dto.SubTaskEntity;
import com.physics.mps.entity.dto.TaskComputeResultEntity;
import com.physics.mps.entity.dto.TaskResultVtuEntity;
import com.physics.mps.entity.enums.CodeEnum;
import com.physics.mps.entity.enums.ScriptStatusEnum;
import com.physics.mps.entity.vo.*;
import com.physics.mps.entity.vo.api.Result;
import com.physics.mps.entity.vo.api.UploadVo;
import com.physics.mps.entity.vo.result.AbaqusDispVo;
import com.physics.mps.entity.vo.result.AbaqusResultVo;
import com.physics.mps.entity.vo.result.AbaqusTempVo;
import com.physics.mps.entity.vo.result.AbstractAbaqusResultVo;
import com.physics.mps.mapper.AppModuleMapper;
import com.physics.mps.module.abaqus.AbaqusComputeService;
import com.physics.mps.module.abaqus.AbstractAbaqusComputeHandler;
import com.physics.mps.module.checker.AbaqusComputeChecker;
import com.physics.mps.module.checker.AnsysComputeChecker;
import com.physics.mps.module.checker.ComputeChecker;
import com.physics.mps.module.checker.ComsolComputeChecker;
import com.physics.mps.module.fastcae.builder.FastcaeMeshBuilder;
import com.physics.mps.module.fastcae.vo.FastcaeMeshData;
import com.physics.mps.module.task.sub.SubTaskService;
import com.physics.mps.module.util.JsonResolver;
import com.physics.mps.module.util.PyUtils;
import com.physics.mps.module.util.ScriptExtractor;
import com.physics.mps.module.util.TaskMonitor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

/**
 * @author mcsoft
 * @since 2023/9/12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ComputeServiceImpl extends AbaqusComputeService {

    private final AppModuleMapper appModuleMapper;

    private final TaskComputeResultService taskComputeResultService;

    private final SubTaskService subTaskService;

    private final TaskResultVtuService taskResultVtuService;

    @Value("${simulate.abaqus.install-path}")
    private String installPath;

    @Value("${simulate.abaqus.output-path}")
    private String outputPath;

    @Value("${simulate.abaqus.fastCae-work-path}")
    private String fastCaeWorkPath;

    @Value("${simulate.abaqus.fastCae-temp-path}")
    private String fastCaeTempPath;

    @Value("${simulate.abaqus.fastCae-stp-path}")
    private String fastCaeStpPath;

    @Value("${simulate.abaqus.cae-path}")
    private String caePath;

    @Value("${simulate.abaqus.script-path}")
    private String scriptPath;

    @Value("${simulate.abaqus.odbToVtu-path}")
    private String odbToVtuPath;

    @Value("${simulate.abaqus.fastCae-exe-path}")
    private String fastCaeExePath;

    @Value("${simulate.openfoam.export-path}")
    private String openfoamExport;

    @Value("${simulate.openfoam.example-path}")
    private String openfoamExample;

    @Value("${simulate.openfoam.ubuntu-path}")
    private String ubuntuPath;

    @Value("${simulate.openfoam.fastCae-dir}")
    private String uFastCAEDir;

    private final JsonResolver js;

    private final ExecutorService readScriptExecutorService;

    private final ExecutorService executeScriptExecutorService;

    private final ExecutorService runOdbToVtuCmdExecutorService;

    private final ExecutorService readOdbToVtuCmdExecutorService;


    private final ApiProperties apiProperties;

    private final MinioUtil minioUtil;


    public ResponseResult compute(StartComputeVo startComputeVo) throws Exception {
//        String content = readFileContent(file);
        AbaqusComputeParam abaqusParam = getAbaqusComputeParam(startComputeVo);
        String s = executeHandler(startComputeVo.getAppModuleId(), abaqusParam);
        List<String> list = writeToPy(abaqusParam.getJobVo().getJobName(), s);
        return executeCommand(list, abaqusParam, startComputeVo);
    }

    @NotNull
    private AbaqusComputeParam getAbaqusComputeParam(StartComputeVo startComputeVo) {
        log.info("=== 主程序发送参数{} ===", JSONUtil.toJsonStr(startComputeVo));
        log.info("=== abaqus 仿真主程序发送参数{} ===", JSONUtil.toJsonStr(startComputeVo));
        Map<String, Object> treeMap = new TreeMap<>(startComputeVo.getParam());
        AbaqusComputeParam abaqusParam = parseJsonToParam(startComputeVo.getAppModuleId(), JSONUtil.toJsonStr(treeMap));
        log.info(abaqusParam.toString());
        AssertUtil.isBlank(startComputeVo.getTaskName(), CodeEnum.TASK_NAME_NOT_NULL);
        ComputeChecker checker = configChecker(startComputeVo);
        checker.check();
        abaqusParam.getJobVo().setJobName(startComputeVo.getTaskName() + "-" + IdUtil.simpleUUID());
        abaqusParam.getJobVo().setAppId(startComputeVo.getAppId());
        return abaqusParam;
    }

    private ComputeChecker configChecker(StartComputeVo startComputeVo) {
        String appId = startComputeVo.getAppId();
        ComputeChecker checker;
        switch (appId) {
            case "1":
                SimulatePathVo simulatePathVo = new SimulatePathVo();
                simulatePathVo.setInstallPath(installPath);
                simulatePathVo.setOutputPath(outputPath);
                simulatePathVo.setScriptPath(scriptPath);
                simulatePathVo.setCaePath(caePath);
                checker = new AbaqusComputeChecker();
                checker.setSimulatePathVo(simulatePathVo);
                break;
            case "2":
                checker = new ComsolComputeChecker();
                break;
            case "3":
                checker = new AnsysComputeChecker();
                break;
            case "4":
                SimulatePathVo simulatePathVo2 = new SimulatePathVo();
                simulatePathVo2.setInstallPath(installPath);
                simulatePathVo2.setOutputPath(outputPath);
                simulatePathVo2.setScriptPath(scriptPath);
                simulatePathVo2.setCaePath(caePath);
                checker = new AbaqusComputeChecker();
                checker.setSimulatePathVo(simulatePathVo2);
                break;
            default:
                throw new BusinessException(CodeEnum.APP_ID_NOT_EXIST);
        }
        return checker;
    }

    private ResponseResult executeCommand(List<String> list, AbaqusComputeParam abaqusParam, StartComputeVo startComputeVo) {
        ProcessBuilder processBuilder = getAbaqusProcessBuilder(list);

        String token = LoginUtil.getToken();

        // 子任务未提前创建则先创建，否则关联已创建的子任务
        SubTaskEntity subTaskEntity = getSubTaskEntity(startComputeVo);


        StringBuilder logContent = new StringBuilder();

        CompletableFuture.runAsync(() -> {
            try {
                LoginUtil.setToken(token);
                processBuilder.redirectErrorStream(true);

                // 启动进程
                Process process = processBuilder.start();

                Future<?> outputFuture = getOutPutFuture(logContent, process);

                checkInpCreate(abaqusParam, subTaskEntity, process);


                process.waitFor();
                // Wait for the outputFuture to complete
                outputFuture.get();
                determinedSuccess(abaqusParam, subTaskEntity, logContent, startComputeVo);

                //读取结果文件，封装为AbaqusResultVo
//                AbaqusResultVo resultVo = generateAbaqusResultVo(abaqusParam, startComputeVo);
            } catch (InterruptedException e) {
                log.error(e.getMessage());
                // 恢复中断状态
                Thread.currentThread().interrupt();
                // 更新任务表状态为“异常”
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } catch (Exception e) {
                // 更新任务表状态为“异常”
                log.error(e.getMessage());
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } finally {
                supplyLog(abaqusParam.getJobVo().getJobName(), logContent);
                subTaskEntity.setCmdLog(logContent.toString());
                subTaskService.saveOrUpdate(subTaskEntity);
                LoginUtil.removeAll();
            }
        }, executeScriptExecutorService);

        return ResponseResult.success();
    }

    @NotNull
    private ProcessBuilder getAbaqusProcessBuilder(List<String> list) {
        String builder = " cd " +
                list.get(0) +
                " && " +
                " abaqus cae noGUI=" +
                list.get(1);
        return new ProcessBuilder("cmd.exe", "/c", builder);
    }

    private String upload2FileSystem(String path, CodeEnum taskVtuUploadFailed) {
        String filename = Paths.get(path).getFileName().toString();
        ResponseEntity<String> upload = minioUtil.upload(filename, apiProperties.getModuleName(), path);
        if (upload.getStatusCode() != HttpStatus.OK) {
            throw new BusinessException(taskVtuUploadFailed);
        }
        return upload.getBody();
    }

    private void determinedSuccess(AbaqusComputeParam abaqusParam, SubTaskEntity subTaskEntity, StringBuilder logContent, StartComputeVo startComputeVo) {
        // set cmd日志
        subTaskEntity.setCmdLog(logContent.toString());
        // set log日志位置
        String logPath = outputPath + "\\\\" + abaqusParam.getJobVo().getJobName() + ".log";
        // 1.判断log文件是否生成，未生成抛出错误
        if (!FileUtil.exist(logPath)) {
            throw new BusinessException(CodeEnum.STP_FILE_OR_PARAM_ERROR);
        }
        subTaskEntity.setLogPath(logPath);

        // keypoint 如果cmd日志包含error,结果生成失败
        if (logContent.toString().toLowerCase().contains(Constants.ERROR)) {
            throw new BusinessException(CodeEnum.FAILED_TO_GENERATE_RESULTS);
        }

        // 2.根据log日志判断是否仿真成功
        String lastLine = TaskMonitor.readLastLine(logPath);
        String template = "Abaqus JOB %s COMPLETED";
        String jobName = abaqusParam.getJobVo().getJobName();
        String result = String.format(template, jobName);
        if (!lastLine.equals(result)) {
            throw new BusinessException(CodeEnum.FAILED_TO_GENERATE_RESULTS);
        }

        // 上传结果文件(新增)
        String resultFilePath = outputPath + "\\\\" + abaqusParam.getJobVo().getJobName() + ".odb";
        ResponseEntity<String> upload = minioUtil.upload(abaqusParam.getJobVo().getJobName() + ".odb", apiProperties.getModuleName(), resultFilePath);
        if (upload.getStatusCode() != HttpStatus.OK) {
            throw new BusinessException(CodeEnum.TASK_FILE_UPLOAD_FAILED);
        }
//        String url = uploadResultFile(resultFilePath);
        String url = upload.getBody();
        subTaskEntity.setStatus(ScriptStatusEnum.COMPLETE.getStatus());

        TaskComputeResultEntity taskComputeResultEntity = new TaskComputeResultEntity();
        taskComputeResultEntity.setFilePath(outputPath + "\\\\" + abaqusParam.getJobVo().getJobName() + ".odb");
        taskComputeResultEntity.setPicPath(outputPath + "\\\\PNG\\\\" + abaqusParam.getJobVo().getJobName() + ".png");

        // 上传pic图片到minio
        String picFilePath = outputPath + "\\\\PNG\\\\" + abaqusParam.getJobVo().getJobName() + ".png";
        ResponseEntity<String> uploadPic = minioUtil.upload(abaqusParam.getJobVo().getJobName() + ".png", apiProperties.getModuleName(), picFilePath);
        if (uploadPic.getStatusCode() != HttpStatus.OK) {
            throw new BusinessException(CodeEnum.TASK_PIC_UPLOAD_FAILED);
        }
        String body = uploadPic.getBody();

        //读取结果文件，封装为AbaqusResultVo
        AbaqusResultVo resultVo = generateAbaqusResultVo(abaqusParam, startComputeVo);

        String targetPath = outputPath + "\\\\" + abaqusParam.getJobVo().getJobName() + ".json";
        try (OutputStream outputStream = Files.newOutputStream(Paths.get(targetPath))) {
            outputStream.write(JSONUtil.toJsonStr(resultVo).getBytes());
            outputStream.flush();
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
        String jsonUrl = upload2FileSystem(targetPath, CodeEnum.TASK_JSON_UPLOAD_FAILED);

        StringBuilder vtu = new StringBuilder();
        writeVtu(vtu, abaqusParam, startComputeVo);

        taskComputeResultEntity.setResultUrl(url);
        taskComputeResultEntity.setPicUrl(body);
        taskComputeResultEntity.setJsonUrl(jsonUrl);
        taskComputeResultEntity.setVtu(vtu.toString());
        taskComputeResultService.save(taskComputeResultEntity);

        subTaskEntity.setComputeResultId(taskComputeResultEntity.getId());

        subTaskService.updateById(subTaskEntity);
    }

    private void checkInpCreate(AbaqusComputeParam abaqusParam, SubTaskEntity subTaskEntity, Process process) {
        while (process.isAlive()) {
            // Check for the .inp file generation abaqus生成了inp文件才是running状态
            String inpFilePath = outputPath + "\\\\" + abaqusParam.getJobVo().getJobName() + ".inp";

            if (FileUtil.exist(inpFilePath)) {
                log.info(".inp file has been generated!");

                subTaskEntity.setStatus(ScriptStatusEnum.RUNNING.getStatus());
                // 修改任务状态
                subTaskService.updateById(subTaskEntity);
                break;
            }
        }
    }

    private Future<?> getOutPutFuture(StringBuilder logContent, Process process) {
        // 读取cmd日志信息
        return getFuture(logContent, process, readScriptExecutorService);
    }

    public List<String> writeToPy(String fileName, String content) {
        String fileExtension = ".py";
        String filePath = scriptPath + fileName + fileExtension;
        log.info("=====脚本文件生成路径 {}=====", filePath);
        try {
            Files.write(Paths.get(filePath), content.getBytes());
        } catch (IOException e) {
            throw new BusinessException(CodeEnum.FILE_WRITE_FAILED);
        }
        List<String> strings = new ArrayList<>();
        strings.add(scriptPath);
        strings.add(fileName + fileExtension);
        return strings;
    }

    private String readFileContent(MultipartFile file) throws BusinessException {
        if (file.isEmpty()) {
            throw new BusinessException(CodeEnum.FILE_IS_NULL);
        }

        StringBuilder contentBuilder = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                contentBuilder.append(line).append("\n");
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BusinessException(CodeEnum.FILE_READ_FAILED);
        }
        return contentBuilder.toString();
    }

    private AbaqusComputeParam parseJsonToParam(String appModuleId, String content) throws BusinessException {
        String methodName = getMethodNameFromDb(appModuleId);
        try {
            Method method = js.getClass().getMethod(methodName, String.class, Integer.class);
            return (AbaqusComputeParam) method.invoke(js, content, 1);
        } catch (InvocationTargetException e) {
            Throwable cause = e.getTargetException();
            if (cause instanceof BusinessException) {
                throw (BusinessException) cause;
            }
            log.error(cause.getMessage(), cause);
            throw new BusinessException(CodeEnum.METHOD_INVOCATION_FAILED);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BusinessException(e.getMessage());
        }
    }

    private String executeHandler(String appModuleId, AbaqusComputeParam abaqusParam) throws BusinessException {
        try {
            String handlerClassName = getHandlerClassNameFromDb(appModuleId);
            AbstractAbaqusComputeHandler handler = (AbstractAbaqusComputeHandler) Class.forName(handlerClassName).getDeclaredConstructor().newInstance();
            handler.setCaePath(caePath + abaqusParam.getJobVo().getJobName());
            handler.setStpPath(abaqusParam.getStpPath());
            handler.setWorkDir(outputPath);
            handler.setAppModuleName(appModuleId);
            handler.setAbaqusComputeParam(abaqusParam);
            String command = handler.createCommand();
            log.info(command);
            return command;
        } catch (InvocationTargetException e) {
            Throwable cause = e.getTargetException();
            if (cause instanceof BusinessException) {
                throw (BusinessException) cause;
            }
            log.error(cause.getMessage(), cause);
            throw new BusinessException(CodeEnum.METHOD_INVOCATION_CREATE_COMMAND_FAILED);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BusinessException(CodeEnum.METHOD_INVOCATION_CREATE_COMMAND_FAILED);
        }
    }

    private String getMethodNameFromDb(String appModuleId) {
        return appModuleMapper.getMethodNameFromDB(appModuleId);
    }

    private String getHandlerClassNameFromDb(String appModuleId) {
        return appModuleMapper.getHandlerClassNameFromDB(appModuleId);
    }

    // ~ Variable & Constant
    // ========================================================================================================


    // ~ Public method
    // ========================================================================================================


    // ~ Override method
    // ========================================================================================================


    // ~ Private method
    // ========================================================================================================
    private AbaqusResultVo generateAbaqusResultVo(AbaqusComputeParam param, StartComputeVo computeVo) {
        AbaqusResultVo abaqusResultVo = new AbaqusResultVo();
        switch (computeVo.getAppModuleId()) {
            case "2":
                abaqusResultVo = generateAbaqusDispResultVo(param, computeVo.getAppModuleId());
                break;
            case "3":
                abaqusResultVo = generateAbaqusDispResultVo(param, computeVo.getAppModuleId());
                break;
            default:
                abaqusResultVo = generateAbaqusTempResultVo(param, computeVo.getAppModuleId());
                break;
        }
        return abaqusResultVo;
    }

    private AbaqusResultVo generateAbaqusTempResultVo(AbaqusComputeParam param, String appModuleId) {
        AbaqusResultVo resultVo = new AbaqusResultVo();
        resultVo.setType(appModuleMapper.getModuleName(appModuleId));

        String path = outputPath + "\\\\PNG\\\\" + param.getJobVo().getJobName() + ".txt";
        Map<String, AbaqusTempVo> tempResultVoMap = new HashMap<>();
        List<AbstractAbaqusResultVo> component = new ArrayList<>();
        List<AbstractAbaqusResultVo> board = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            String line;
            while ((line = reader.readLine()) != null) {
                String[] s = line.split(":");
                switch (s[1]) {
                    case "max_val":
                        AbaqusTempVo vo = tempResultVoMap.getOrDefault(s[0], new AbaqusTempVo());
                        vo.setName(s[0]);
                        vo.setMaxTemperature(s[2]);
                        tempResultVoMap.put(s[0], vo);
                        break;
                    case "min_val":
                        AbaqusTempVo vo2 = tempResultVoMap.getOrDefault(s[0], new AbaqusTempVo());
                        vo2.setName(s[0]);
                        vo2.setMinTemperature(s[2]);
                        tempResultVoMap.put(s[0], vo2);
                        break;
                    default:
                        break;
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BusinessException(CodeEnum.FILE_READ_FAILED);
        }
        tempResultVoMap.forEach((k, v) -> {
            if ("BOARD".equals(k)) {
                board.add(v);
            } else {
                component.add(v);
            }
        });
        resultVo.setBoard(board);
        resultVo.setComponent(component);
        return resultVo;
    }

    private AbaqusResultVo generateAbaqusDispResultVo(AbaqusComputeParam param, String appModuleId) {
        AbaqusResultVo resultVo = new AbaqusResultVo();
        resultVo.setType(appModuleMapper.getModuleName(appModuleId));
        String path = outputPath + "\\\\PNG\\\\" + param.getJobVo().getJobName() + ".txt";
        Map<String, AbaqusDispVo> dispResultVos = new HashMap<>();
        List<AbstractAbaqusResultVo> component = new ArrayList<>();
        List<AbstractAbaqusResultVo> board = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            String line;
            while ((line = reader.readLine()) != null) {
                String[] s = line.split(":");
                AbaqusDispVo vo = dispResultVos.getOrDefault(s[0], new AbaqusDispVo());
                vo.setName(s[0]);
                vo.setDisplacement(s[2]);
                dispResultVos.put(s[0], vo);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new BusinessException(CodeEnum.FILE_READ_FAILED);
        }
        dispResultVos.forEach((k, v) -> {
            if ("BOARD".equals(k)) {
                board.add(v);
            } else {
                component.add(v);
            }
        });
        resultVo.setComponent(component);
        resultVo.setBoard(board);
        return resultVo;
    }


    private void supplyLog(String jobName, StringBuilder builder) {
        String msgPath = outputPath + "\\\\" + jobName + ".msg";
        if (FileUtil.exist(msgPath)) {
            try (BufferedReader reader = new BufferedReader(new FileReader(msgPath))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    if (line.contains("ERROR")) {
                        builder.append(line).append(PyUtils.WRAP);
                    }
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                throw new BusinessException(CodeEnum.FILE_READ_FAILED);
            }
        }
    }

    /**
     * 上传结果文件
     *
     * @param local
     * @return
     */
    private String uploadResultFile(String local) {
        Map<String, String> params = new HashMap<>(8);
        File file = new File(local);

        Map<String, String> urls = apiProperties.getUrls();
        String url = urls.get("upload-file");
        params.put("moduleName", apiProperties.getModuleName());
        params.put("accessKey", apiProperties.getAccessKey());
        params.put("secretKey", apiProperties.getSecretKey());

        try (Response response = OkHttpUtil.uploadFile(url, file, params);
             ResponseBody responseBody = response.body()) {
            AssertUtil.isNull(responseBody, CodeEnum.UPLOAD_EXCEPTION);
            String body = new String(responseBody.bytes());
            Result<UploadVo> result = JSONUtil.toBean(body, new TypeReference<Result<UploadVo>>() {
            }, false);
            AssertUtil.isFalse(CodeEnum.UPLOAD_EXCEPTION, result.getCode() == 200);
            UploadVo resultData = result.getData();
            return resultData.getUrl();
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
    }

    private void writeVtu(StringBuilder builder, AbaqusComputeParam abaqusParam, StartComputeVo startComputeVo) {
        //--instance "BEAMQ-1" --step "Step-1:-1" "Step-2:-1"
        builder.append("--instance ");
        List<AbaqusPartVo> parts = abaqusParam.getParts();
        for (AbaqusPartVo part : parts) {
            builder.append("\"").append(part.getName()).append("\" ");
        }
        builder.append("--step \"Step-1:-1\"");
        if ("2".equals(startComputeVo.getAppModuleId()) || "3".equals(startComputeVo.getAppModuleId())) {
            builder.append(" \"Step-2:-1\" ");
        }
    }

    public ResponseResult runOdbToVtu(String subTaskId) {
//        SubTaskEntity byId = subTaskService.getById("7856396278c077a7a67d74b0d0e50dd2");
        SubTaskEntity byId = subTaskService.getById(subTaskId);
        /*
        TaskComputeResultEntity resultEntity = taskComputeResultService.getById(byId.getComputeResultId());
        if (resultEntity != null) {
            resultEntity.setVtuType(3);
            taskComputeResultService.saveOrUpdate(resultEntity);
        }
         */
        AssertUtil.isNull(byId, CodeEnum.SUBTASK_NOT_NULL);

        // 文件的 URL 地址
        String fileUrl = byId.getTaskComputeResult().getResultUrl();

        // 提取文件名，包括后缀
        String fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);

        // 本地保存路径，包括文件名和后缀
        String destinationPath = odbToVtuPath + fileName;

        // 确保目标文件夹存在
        FileUtil.mkdir(new File(destinationPath).getParentFile());

        try {
            HttpUtil.downloadFile(fileUrl, destinationPath);
            log.info("文件下载成功，保存路径：" + destinationPath);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件下载失败：" + e.getMessage());
            throw new BusinessException(CodeEnum.DOWNLOAD_EXCEPTION);
        }

//        ProcessBuilder processBuilder1 = new ProcessBuilder("cmd.exe", "/c", jsonBuilder);

        StringBuilder logContent = new StringBuilder();

        List<String> vtuPaths = new ArrayList<>();

        CompletableFuture.runAsync(() -> {
            List<Path> paths = new ArrayList<>();
            try {
//              检查当前执行目录
                log.info(System.getProperty("user.dir"));
                Path scriptPath = ScriptExtractor.extractScript("python/odb2vtk.py");
                paths.add(scriptPath);
                paths.add(ScriptExtractor.extractScript("python/utilities.py"));
                paths.add(ScriptExtractor.extractScript("python/multiprocess.py"));
                String builder = "abaqus python " + scriptPath + " --header 0 " + byId.getTaskComputeResult().getVtu() +
                        " --odbFile " + destinationPath + " --outputFolder " + odbToVtuPath;
                log.info("===脚本 {}===", builder);
//              log.info("===转换json脚本 {}===", jsonBuilder);
                ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", builder);
//                processBuilder1.redirectErrorStream(true);

                processBuilder.redirectErrorStream(true);
////                log.info(System.getProperty("user.dir"));
//
//                // 启动进程
                Process process = processBuilder.start();

                Future<?> outputFuture = getOdbToVtuOutPut(logContent, process, vtuPaths);


                process.waitFor();
                // Wait for the outputFuture to complete
                outputFuture.get();

                boolean matchesFormat = isLastLineExpectedFormat(logContent);
                if (matchesFormat) {
                    for (String vtuPath : vtuPaths) {
                        TaskResultVtuEntity taskResultVtuEntity = new TaskResultVtuEntity();
                        taskResultVtuEntity.setComputeResultId(byId.getTaskComputeResult().getId());
                        String vtuFileName = Paths.get(vtuPath).getFileName().toString();
                        taskResultVtuEntity.setVtuName(vtuFileName);
                        String s = upload2FileSystem(vtuPath, CodeEnum.TASK_VTU_UPLOAD_FAILED);
                        taskResultVtuEntity.setVtuUrl(s);
                        taskResultVtuService.save(taskResultVtuEntity);
                    }
                }
            } catch (InterruptedException e) {
                log.error(e.getMessage());
                // 恢复中断状态
                Thread.currentThread().interrupt();
            } catch (Exception e) {
                // 更新任务表状态为“异常”
                log.error(e.getMessage());
            } finally {
                for (Path path : paths) {
                    safelyDeleteScript(path);
                }
                // 删除临时目录
                safelyDeleteScript(ScriptExtractor.getTempDir());
            }
        }, runOdbToVtuCmdExecutorService);
        return ResponseResult.success();
    }

    private void safelyDeleteScript(Path scriptPath) {
        if (scriptPath != null) {
            try {
                // 使用 HuTool 的 FileUtil 来删除文件
                FileUtil.del(scriptPath.toFile());
            } catch (Exception e) {
                log.error("Error deleting script file: " + e.getMessage());
            }
        }
    }

    private String getLastGeneratedFilePath(StringBuilder logContent) {
        // 正则表达式匹配模式：[任意非空白字符序列].json generated
        String regexPattern = ".*\\.json generated";

        String[] lines = logContent.toString().split(System.lineSeparator());
        if (lines.length > 0) {
            String lastLine = lines[lines.length - 1];
            if (lastLine.matches(regexPattern)) {
                // 移除末尾的 " generated"，返回文件的全路径
                return lastLine.replace(" generated", "");
            }
        }
        return null; // 如果没有匹配，返回 null
    }

    private Future<?> getOdbJsonOutPut(StringBuilder logContent, Process process) {
        return getFuture(logContent, process, readOdbToVtuCmdExecutorService);
    }

    @NotNull
    private Future<?> getFuture(StringBuilder logContent, Process process, ExecutorService readOdbToVtuCmdExecutorService) {
        return readOdbToVtuCmdExecutorService.submit(() -> {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    log.info(line);
                    logContent.append(line).append(System.lineSeparator());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }


    private boolean isLastLineExpectedFormat(StringBuilder logContent) {
        // 正则表达式匹配模式：--- [数字（可能包括小数点）] seconds complete---
        String regexPattern = "--- \\d+(\\.\\d+)? seconds complete---";

        String[] lines = logContent.toString().split(System.lineSeparator());
        if (lines.length > 0) {
            String lastLine = lines[lines.length - 1];
            return lastLine.matches(regexPattern);
        }
        return false;
    }

    private Future<?> getOdbToVtuOutPut(StringBuilder logContent, Process process, List<String> vtuPaths) {
        // 读取cmd日志信息
        return readOdbToVtuCmdExecutorService.submit(() -> {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    log.info(line);
                    if (line.startsWith("Writing to vtu ")) {
                        String filePath = line.substring("Writing to vtu ".length());
                        vtuPaths.add(filePath);
                    }
                    logContent.append(line).append(System.lineSeparator());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * fastcae进行计算
     * @param startComputeVo
     * @return
     */
    public ResponseResult fastCaeCompute(StartComputeVo startComputeVo) {
        //获取计算参数
        AbaqusComputeParam abaqusParam = getAbaqusComputeParam(startComputeVo);
        //获取脚本内容
        String s = executeHandler(startComputeVo.getAppModuleId(), abaqusParam);
        //脚本内容写入文件
        List<String> list = writeToPy(abaqusParam.getJobVo().getJobName(), s);
        return runFastCaeCompute(list, abaqusParam, startComputeVo);
    }

    /**
     * fastcae启动进程计算
     * @param list 脚本路径
     * @param abaqusParam 计算参数
     * @param startComputeVo 任务参数
     * @return
     */
    private ResponseResult runFastCaeCompute(List<String> list, AbaqusComputeParam abaqusParam, StartComputeVo startComputeVo) {
        ProcessBuilder processBuilder = getAbaqusProcessBuilder(list);

        String token = LoginUtil.getToken();
        SubTaskEntity subTaskEntity = getSubTaskEntity(startComputeVo);


        StringBuilder logContent = new StringBuilder();

        CompletableFuture.runAsync(() -> {
            try {
                LoginUtil.setToken(token);
                processBuilder.redirectErrorStream(true);

                // 启动进程
                Process process = processBuilder.start();

                Future<?> outputFuture = getOutPutFuture(logContent, process);

                process.waitFor();
                // Wait for the outputFuture to complete
                outputFuture.get();

                String copyInput = copyInput(abaqusParam, subTaskEntity);

                if (copyInput == null) {
                    throw new BusinessException(CodeEnum.INPUT_GENERATION_FAILURE);
                }

                // 创建算例
                String createCase = "ControlPanel.createCase(\"" + abaqusParam.getJobVo().getJobName() + "\",\"CreateAbaqusPlugin\")";
                String writeCreateCasePy = writeCreateCasePy(abaqusParam.getJobVo().getJobName(), createCase);
//                FastCAE.exe -i test4.py -a D:\test\job\json-27867a9c56d24f64953764769cc87a1d.inp -o E:\tc\a\job\Case_4\input.inp -nogui
                String caeBuilder = fastCaeExePath + " -i " + writeCreateCasePy + " -a " + outputPath + "\\\\" + abaqusParam.getJobVo().getJobName() + ".inp" + " -o " + copyInput + " -nogui";

                ProcessBuilder caeProcessBuilder = new ProcessBuilder("cmd.exe", "/c", caeBuilder);
                caeProcessBuilder.redirectErrorStream(true);

                // 启动进程
                Process caeProcess = caeProcessBuilder.start();
                Future<?> caeOutPut = getOutPutFuture(logContent, caeProcess);

                caeProcess.waitFor();
                // Wait for the outputFuture to complete
                caeOutPut.get();

                //判断计算是否成功，成功则进入结果处理，失败则抛出异常
                determinedFastCaeSuccess(abaqusParam, subTaskEntity, logContent, startComputeVo);

            } catch (InterruptedException e) {
                log.error(e.getMessage());
                // 恢复中断状态
                Thread.currentThread().interrupt();
                // 更新任务表状态为“异常”
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } catch (Exception e) {
                // 更新任务表状态为“异常”
                log.error(e.getMessage());
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } finally {
                supplyFastCaeLog(abaqusParam.getJobVo().getJobName(), logContent);
                subTaskEntity.setCmdLog(logContent.toString());
                subTaskService.saveOrUpdate(subTaskEntity);
                LoginUtil.removeAll();
            }
        }, executeScriptExecutorService);

        return ResponseResult.success();
    }

    /**
     * 创建并获取子任务对象
     * @param startComputeVo 任务参数
     * @return
     */
    @NotNull
    private SubTaskEntity getSubTaskEntity(StartComputeVo startComputeVo) {
        // 子任务未提前创建则先创建，否则关联已创建的子任务
        SubTaskEntity subTaskEntity;

        if (StringUtils.isEmpty(startComputeVo.getSubTaskId())) {
            // 创建子任务
            SubTaskEntity subTask = new SubTaskEntity();
            subTask.setName(startComputeVo.getTaskName() + "-" + IdUtil.simpleUUID());
            subTask.setStatus(ScriptStatusEnum.INIT.getStatus());
            subTask.setUserId(Objects.requireNonNull(LoginUtil.getUserId()).toString());
            subTask.setTaskId(IdUtil.fastSimpleUUID());
            subTask.setAppId(startComputeVo.getAppId());
            subTask.setModuleId(startComputeVo.getAppModuleId());
            subTaskService.save(subTask);

            subTaskEntity = subTask;
        } else {
            subTaskEntity = subTaskService.getBaseMapper().selectById(startComputeVo.getSubTaskId());
        }
        AssertUtil.isNull(subTaskEntity, CodeEnum.SUBTASK_NOT_NULL);
        return subTaskEntity;
    }

    /**
     * 获取计算日志
     * @param jobName 任务名称
     * @param logContent 计算进程中的cmd日志
     */
    private void supplyFastCaeLog(String jobName, StringBuilder logContent) {
        //读取msg计算结果文件，记录错误行
        String msgPath = fastCaeWorkPath + jobName + File.separator + jobName + ".msg";
        if (FileUtil.exist(msgPath)) {
            try (BufferedReader reader = new BufferedReader(new FileReader(msgPath))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    if (line.contains("ERROR")) {
                        logContent.append(line).append(PyUtils.WRAP);
                    }
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
                throw new BusinessException(CodeEnum.FILE_READ_FAILED);
            }
        }
    }

    /**
     * 判断fastcae是否计算成功
     * @param abaqusParam 计算参数
     * @param subTaskEntity 任务对象
     * @param logContent 计算进程cmd日志
     * @param startComputeVo 任务参数
     */
    private void determinedFastCaeSuccess(AbaqusComputeParam abaqusParam, SubTaskEntity subTaskEntity, StringBuilder logContent, StartComputeVo startComputeVo) {
        // set cmd日志
        subTaskEntity.setCmdLog(logContent.toString());

        // keypoint 如果cmd日志包含error,结果生成失败
//        if (logContent.toString().toLowerCase().contains(Constants.ERROR)) {
//            throw new BusinessException(CodeEnum.FAILED_TO_GENERATE_RESULTS);
//        }

        String staPath = fastCaeWorkPath + abaqusParam.getJobVo().getJobName() + File.separator + "input.sta";


        // 读取sta文件的内容
        String staFileContent = FileUtil.readString(staPath, StandardCharsets.UTF_8);

        // 检查文件内容是否包含特定的字符串
        if (!staFileContent.contains("THE ANALYSIS HAS COMPLETED SUCCESSFULLY")) {
            throw new BusinessException(CodeEnum.FAILED_TO_GENERATE_RESULTS);
        }

        // 上传结果文件(新增)
        String inputOdb = fastCaeWorkPath + abaqusParam.getJobVo().getJobName() + File.separator + "input.odb";
        if (FileUtil.exist(inputOdb)) {
            FileUtil.rename(new File(inputOdb), abaqusParam.getJobVo().getJobName() + ".odb", false);
        } else {
            throw new BusinessException(CodeEnum.FILE_IS_NULL);
        }
        String resultFilePath = fastCaeWorkPath + abaqusParam.getJobVo().getJobName() + File.separator + abaqusParam.getJobVo().getJobName() + ".odb";
        ResponseEntity<String> upload = minioUtil.upload(abaqusParam.getJobVo().getJobName() + ".odb", apiProperties.getModuleName(), resultFilePath);
        if (upload.getStatusCode() != HttpStatus.OK) {
            throw new BusinessException(CodeEnum.TASK_FILE_UPLOAD_FAILED);
        }
//        String url = uploadResultFile(resultFilePath);
        String url = upload.getBody();
        subTaskEntity.setStatus(ScriptStatusEnum.COMPLETE.getStatus());

        TaskComputeResultEntity taskComputeResultEntity = new TaskComputeResultEntity();
        taskComputeResultEntity.setFilePath(resultFilePath);

        /*
        //读取结果文件，封装为AbaqusResultVo
        AbaqusResultVo resultVo = generateAbaqusResultVo(abaqusParam, startComputeVo);

        String targetPath = fastCaeWorkPath + abaqusParam.getJobVo().getJobName() + File.separator + abaqusParam.getJobVo().getJobName() + ".json";
        try (OutputStream outputStream = Files.newOutputStream(Paths.get(targetPath))) {
            outputStream.write(JSONUtil.toJsonStr(resultVo).getBytes());
            outputStream.flush();
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }


        String jsonUrl = upload2FileSystem(targetPath, CodeEnum.TASK_JSON_UPLOAD_FAILED);
        */
        //将odb转换为vtu的指令存入数据库
        StringBuilder vtu = new StringBuilder();
        writeVtu(vtu, abaqusParam, startComputeVo);

        taskComputeResultEntity.setResultUrl(url);
        //taskComputeResultEntity.setJsonUrl(jsonUrl);
        taskComputeResultEntity.setVtu(vtu.toString());
        taskComputeResultEntity.setVtuType(3);
        taskComputeResultService.save(taskComputeResultEntity);

        subTaskEntity.setComputeResultId(taskComputeResultEntity.getId());

        subTaskService.updateById(subTaskEntity);

        //将odb文件转换为vtu文件
        //runOdbToVtu(subTaskEntity.getId());
        runOdbToVtu(subTaskEntity.getId(), resultFilePath);
    }

    /**
     * 创建算例脚本
     * @param jobName 任务名称
     * @param createCase 脚本内容
     * @return
     */
    private String writeCreateCasePy(String jobName, String createCase) {
        String fileExtension = ".py";
        String filePath = fastCaeWorkPath + jobName + File.separator + jobName + fileExtension;
        log.info("=====算例脚本文件生成路径 {}=====", filePath);
        try {
            Files.write(Paths.get(filePath), createCase.getBytes());
        } catch (IOException e) {
            throw new BusinessException(CodeEnum.FILE_WRITE_FAILED);
        }
        return filePath;
    }

    /**
     * 复制input文件到指定文件夹
     * @param abaqusParam 计算参数
     * @param subTaskEntity 任务对象
     * @return
     */
    private String copyInput(AbaqusComputeParam abaqusParam, SubTaskEntity subTaskEntity) {
        // Check for the .inp file generation abaqus生成了inp文件才是running状态
        String inpFilePath = outputPath + "\\\\" + abaqusParam.getJobVo().getJobName() + ".inp";

        if (FileUtil.exist(inpFilePath)) {
            log.info(".inp file has been generated!");
            // 指定文件夹路径
            String folderPath = fastCaeWorkPath + abaqusParam.getJobVo().getJobName();
            // 检查文件夹是否存在
            if (!FileUtil.exist(folderPath)) {
                // 如果不存在，则创建新的文件夹
                FileUtil.mkdir(folderPath);
            }
                /*
                // 定义目标文件路径
                String destFilePath = folderPath + File.separator + abaqusParam.getJobVo().getJobName() + ".inp";

                // 复制文件
                FileUtil.copy(inpFilePath, destFilePath, true);

                */
            subTaskEntity.setStatus(ScriptStatusEnum.RUNNING.getStatus());
            // 修改任务状态
            subTaskService.updateById(subTaskEntity);
            return folderPath + File.separator + "input.inp";
        }
        return null;
    }

    /**
     * 划分网格模块
     * @param meshData
     * @return
     */
    public ResponseResult fastCaeMesh(FastcaeMeshData meshData) {
        return runFastCaeMesh(meshData);
    }

    /**
     * 执行划分网格进程
     * @param meshData 网格参数
     * @return
     */
    private ResponseResult runFastCaeMesh(FastcaeMeshData meshData) {
        String token = LoginUtil.getToken();
        SubTaskEntity subTaskEntity = getSubTaskEntity(meshData);

        StringBuilder logContent = new StringBuilder();
        StringBuilder resultUrl = new StringBuilder();
        log.info("=== mesh仿真主程序发送参数{} ===", JSONUtil.toJsonStr(meshData));
        CompletableFuture.runAsync(() -> {
            try {
                LoginUtil.setToken(token);

                //更新网格参数的工作目录
                meshData.modify(fastCaeWorkPath + "mesh\\");
                String downloadPath = meshData.getStpPath();
                //String[] stpPathArr = downloadPath.split("/");
                //String stpName = stpPathArr[stpPathArr.length - 1];
                //stpName = stpName.split("\\.")[0] + IdUtil.fastSimpleUUID() + ".stp";
                String newStpPath = fastCaeStpPath + IdUtil.fastSimpleUUID() + ".stp";
                //下载模型到服务器本地
                OkHttpUtil.download(downloadPath, newStpPath);
                meshData.setStpPath(newStpPath);

                if (!FileUtil.exist(meshData.getStpPath())) {
                    throw new BusinessException(CodeEnum.STP_FILE_NOT_EXIST);
                }

                //生成网格脚本并写入文件
                log.info("========网格脚本生成路径 {}========", meshData.getMeshPath() + ".py");
                FastcaeMeshBuilder meshBuilder = new FastcaeMeshBuilder(meshData);
                String meshPyPath = meshBuilder.writeToPy(meshData.getMeshPath() + ".py");
                String meshCommand = fastCaeExePath + " -i " + meshPyPath + " -nogui";

                subTaskEntity.setStatus(ScriptStatusEnum.RUNNING.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);

                ProcessBuilder caeProcessBuilder = new ProcessBuilder("cmd.exe", "/c", meshCommand);
                caeProcessBuilder.redirectErrorStream(true);

                log.info("========网格脚本提交FastCAE计算开始========");
                // 启动进程
                Process caeProcess = caeProcessBuilder.start();
                Future<?> caeOutPut = getOutPutFuture(logContent, caeProcess);

                caeProcess.waitFor();
                // Wait for the outputFuture to complete
                caeOutPut.get();

                //判断网格划分是否成功
                determinedMeshSuccess(meshData, logContent, subTaskEntity, resultUrl);
            } catch (InterruptedException e) {
                log.error(e.getMessage());
                // 恢复中断状态
                Thread.currentThread().interrupt();
                // 更新任务表状态为“异常”
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } catch (Exception e) {
                // 更新任务表状态为“异常”
                log.error(e.getMessage());
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } finally {
                subTaskEntity.setCmdLog(logContent.toString());
                subTaskService.saveOrUpdate(subTaskEntity);
                LoginUtil.removeAll();
            }
        }, executeScriptExecutorService);

        return ResponseResult.success();
    }

    /**
     * 判断网格划分是否成功，成功则进入下一步操作，失败则抛出异常
     * @param meshData
     * @param logContent
     * @param subTaskEntity
     * @param resultUrl
     */
    private void determinedMeshSuccess(FastcaeMeshData meshData, StringBuilder logContent, SubTaskEntity subTaskEntity, StringBuilder resultUrl) {
        subTaskEntity.setCmdLog(logContent.toString());

        String tempVtuPath = fastCaeTempPath + "mesh.vtk";
        String resultFilePath = meshData.getMeshPath() + ".vtk";

        //判断是否生成vtk文件，如果生成则存储，没有生成则抛出异常
        if (FileUtil.exist(tempVtuPath)) {
            FileUtil.copy(tempVtuPath, resultFilePath, true);
            FileUtil.del(tempVtuPath);
        } else {
            throw new BusinessException(CodeEnum.MESH_GENERATE_FAILED);
        }


        // 上传结果文件(新增)
        ResponseEntity<String> upload = minioUtil.upload(new File(resultFilePath).getName(), apiProperties.getModuleName(), resultFilePath);
        if (upload.getStatusCode() != HttpStatus.OK) {
            throw new BusinessException(CodeEnum.TASK_FILE_UPLOAD_FAILED);
        }
//        String url = uploadResultFile(resultFilePath);
        String url = upload.getBody();
        subTaskEntity.setStatus(ScriptStatusEnum.COMPLETE.getStatus());

        TaskComputeResultEntity taskComputeResultEntity = new TaskComputeResultEntity();
        taskComputeResultEntity.setFilePath(resultFilePath);
        taskComputeResultEntity.setResultUrl(url);
        taskComputeResultEntity.setVtuType(1);
        taskComputeResultService.save(taskComputeResultEntity);

        subTaskEntity.setComputeResultId(taskComputeResultEntity.getId());

        subTaskService.updateById(subTaskEntity);
        resultUrl.append(url);
    }

    /**
     * openfaom计算模块
     * @param startComputeVo 任务参数
     * @return
     */
    public ResponseResult openfoamCompute(StartComputeVo startComputeVo) {
        //startComputeVo.setTaskName(startComputeVo.getTaskName() + "-" + IdUtil.simpleUUID());
        //OpenFoamHandler openFoamHandler = new OpenFoamHandler(startComputeVo.getParam(), openfoamExport + startComputeVo.getTaskName(), openfoamExample);
        return runOpenfoamCompute(startComputeVo);
    }

    /**
     * openfaom计算模块
     * @param startComputeVo 任务参数
     * @return
     */
    public ResponseResult uOpenfoamCompute(StartComputeVo startComputeVo) {
        //OpenFoamHandler openFoamHandler = new OpenFoamHandler(startComputeVo.getParam(), openfoamExport + startComputeVo.getTaskName(), openfoamExample);
        return runUOpenfoamCompute(startComputeVo);
    }

    /**
     * openfoam进入计算
     * @param startComputeVo 任务参数
     * @return
     */
    private ResponseResult runUOpenfoamCompute(StartComputeVo startComputeVo) {
        String token = LoginUtil.getToken();
        SubTaskEntity subTaskEntity = getSubTaskEntity(startComputeVo);
        startComputeVo.setTaskName(startComputeVo.getTaskName() + "-" + IdUtil.simpleUUID());
        OpenFoamHandler openFoamHandler = new OpenFoamHandler(startComputeVo, startComputeVo.getParam(), openfoamExport + startComputeVo.getTaskName(), openfoamExample);
        StringBuilder logContent = new StringBuilder();

        CompletableFuture.runAsync(() -> {
            try {
                LoginUtil.setToken(token);

                //1.生成计算文件
                log.info("========openfoam计算文件生成路径 {}========", openFoamHandler.getExportPath());
                openFoamHandler.generate();

                //2.生成启动openfoam的start.sh文件
                //log.info("========FastCAE启动文件生成路径 {}========", uFastCAEExe + startComputeVo.getTaskName() + ".sh");
                String uFastCAEExe = generateStartSH(openFoamHandler.getExportPath(), "start");
                log.info("========FastCAE启动文件生成路径 {}========", uFastCAEExe + "start.sh");

                subTaskEntity.setStatus(ScriptStatusEnum.RUNNING.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);

                String command = "cd " + uFastCAEExe + " && " + "./" + "start" + ".sh";

                ProcessBuilder processBuilder = new ProcessBuilder();
                processBuilder.command("bash", "-c", command);
                processBuilder.redirectErrorStream(true);

                log.info("========提交FastCAE的OpenFOAM模块计算开始========");
                // 启动进程
                Process caeProcess = processBuilder.start();
                Future<?> caeOutPut = getOutPutFuture(logContent, caeProcess);

                caeProcess.waitFor();
                // Wait for the outputFuture to complete
                caeOutPut.get();

                //将结果文件转换成vtk文件
                String toVtk = "cd " + openFoamHandler.getExportPath() + " && " + "pvbatch pv-vtk.py";
                ProcessBuilder vtkBuilder = new ProcessBuilder();
                vtkBuilder.command("bash", "-c", toVtk);
                vtkBuilder.redirectErrorStream(true);
                Process vtkProcess = vtkBuilder.start();
                Future<?> vtkOutPut = getOutPutFuture(new StringBuilder(), vtkProcess);
                vtkProcess.waitFor();
                // Wait for the outputFuture to complete
                vtkOutPut.get();

                //获取计算结果
                generateResult(logContent.toString(), openFoamHandler, subTaskEntity);
            } catch (InterruptedException e) {
                log.error(e.getMessage());
                // 恢复中断状态
                Thread.currentThread().interrupt();
                // 更新任务表状态为“异常”
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } catch (Exception e) {
                // 更新任务表状态为“异常”
                log.error(e.getMessage());
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } finally {
                FileUtil.del(openfoamExport + startComputeVo.getTaskName());
                subTaskEntity.setCmdLog(logContent.toString());
                subTaskService.saveOrUpdate(subTaskEntity);
                LoginUtil.removeAll();
            }
        }, executeScriptExecutorService);
        return ResponseResult.success();
    }

    /**
     * 生成启动openfoam的start.sh文件，系统启动计算进程脚本
     * @param vmPath
     * @param name
     * @return
     */
    private String generateStartSH(String vmPath, String name) {
        //fastCaeExePath
        StringBuilder builder = new StringBuilder();
        builder.append("# set PYTHONHOME\\PYTHONPATH").append(PyUtils.WRAP);
        builder.append("export PYTHONHOME=$PYTHONHOME:").append(uFastCAEDir).append("output/python37").append(PyUtils.WRAP);
        builder.append("export PYTHONPATH=$PYTHONPATH:").append(uFastCAEDir).append("output/python37/lib/python3.7").append(PyUtils.WRAP);
        builder.append("./FastCAE -vm ").append(vmPath);
        String uFastCAEExe = uFastCAEDir + "output/bin/" + name + ".sh";
        try {
            File file = FileUtil.writeUtf8String(builder.toString(), uFastCAEExe);
        } catch (IORuntimeException e) {
            throw new RuntimeException(e);
        }
        return uFastCAEDir + "output/bin/";
    }

    /**
     * 获取并判断计算结果
     * @param logContent
     * @param openFoamHandler
     * @param subTaskEntity
     */
    private void generateResult(String logContent, OpenFoamHandler openFoamHandler, SubTaskEntity subTaskEntity) {
        //1.将控制台内容写入文件
        File logFile;
        try {
            logFile = FileUtil.writeUtf8String(logContent, openFoamHandler.getExportPath() + "/process.log");
        } catch (IORuntimeException e) {
            throw new BusinessException(CodeEnum.LOG_FILE_NOT_EXIST);
        }

        //2.判断结果文件是否生成
        String log = TaskMonitor.readLastLine(logFile.getPath());
        if (!log.startsWith("Created 'case") && FileUtil.exist(openFoamHandler.getExportPath() + "/VTKS_2000/user.vtm")) {
            throw new BusinessException(CodeEnum.OPENFOAM_ERROR);
        }

        //3.上传结果文件
        List<String> urlLists = new ArrayList<>();
        String vtuPath = openFoamHandler.getExportPath() + "/VTKS_2000/user";
        List<String> list = FileUtil.listFileNames(vtuPath);
        String resultUrl = "";
        for (String vtu : list) {
            //String vtuName = vtu.replace(".vtu", IdUtil.simpleUUID() + ".vtu");
            if ("fluid.vtu".equals(vtu)) continue;
            String vtuName = IdUtil.simpleUUID() + ".vtu";
            ResponseEntity<String> upload = minioUtil.upload(vtuName, apiProperties.getModuleName(), vtuPath + "/" + vtu);
            if (upload.getStatusCode() != HttpStatus.OK) {
                throw new BusinessException(CodeEnum.TASK_FILE_UPLOAD_FAILED);
            }
            String url = upload.getBody();
            if (resultUrl.isEmpty()) resultUrl = url.replace(vtuName, "");
            urlLists.add(vtuName);
        }

        TaskComputeResultEntity taskComputeResultEntity = new TaskComputeResultEntity();
        taskComputeResultEntity.setFilePath(openFoamHandler.getExportPath() + "/VTKS_2000/user/");
        if (resultUrl.isEmpty()) {
            throw new BusinessException(CodeEnum.OPENFOAM_ERROR);
        }
        taskComputeResultEntity.setResultUrl(resultUrl);
        taskComputeResultEntity.setVtu(String.join(",", urlLists));
        taskComputeResultEntity.setVtuType(2);
        taskComputeResultService.save(taskComputeResultEntity);

        subTaskEntity.setComputeResultId(taskComputeResultEntity.getId());
        //4.修改任务状态
        subTaskEntity.setStatus(ScriptStatusEnum.COMPLETE.getStatus());
        subTaskService.updateById(subTaskEntity);
    }

    /**
     * 上传openfoam产生的vtk结果文件
     * @param openFoamHandler
     * @return
     */
    private String uploadOpenFOAMVtks(OpenFoamHandler openFoamHandler) {
        StringBuilder urlBuilder = new StringBuilder();
        String vtuPath = openFoamHandler.getExportPath() + "/VTKS_2000/user";
        List<String> list = FileUtil.listFileNames(vtuPath);

        for (String vtu : list) {
            String vtuName = vtu.replace(".vtu", IdUtil.simpleUUID() + ".vtu");
            ResponseEntity<String> upload = minioUtil.upload(vtuName, apiProperties.getModuleName(), vtuPath + "/" + vtu);
            if (upload.getStatusCode() != HttpStatus.OK) {
                throw new BusinessException(CodeEnum.TASK_FILE_UPLOAD_FAILED);
            }
            String url = upload.getBody();
            urlBuilder.append(url).append(PyUtils.WRAP);
        }

        return urlBuilder.toString();
    }

    /**
     * openfoam进入计算
     * @param startComputeVo 任务参数
     * @return
     */
    private ResponseResult runOpenfoamCompute(StartComputeVo startComputeVo) {
        String token = LoginUtil.getToken();
        SubTaskEntity subTaskEntity = getSubTaskEntity(startComputeVo);

        //1.生成计算文件
        OpenFoamHandler openFoamHandler = new OpenFoamHandler(startComputeVo, startComputeVo.getParam(), openfoamExport + startComputeVo.getTaskName(), openfoamExample);

        StringBuilder logContent = new StringBuilder();

        CompletableFuture.runAsync(() -> {
            try {
                LoginUtil.setToken(token);

                log.info("========openfoam计算文件生成路径 {}========", openFoamHandler.getExportPath());
                openFoamHandler.generate();

                String command = fastCaeExePath + " -openfoam " + openFoamHandler.getExportPath() + " -vm " + openfoamExample + " -nogui";

                ProcessBuilder caeProcessBuilder = new ProcessBuilder("cmd.exe", "/c", command);
                caeProcessBuilder.redirectErrorStream(true);

                log.info("========提交FastCAE的OpenFOAM模块计算开始========");
                // 启动进程
                Process caeProcess = caeProcessBuilder.start();
                Future<?> caeOutPut = getOutPutFuture(logContent, caeProcess);

                caeProcess.waitFor();
                // Wait for the outputFuture to complete
                caeOutPut.get();

                //determinedMeshSuccess(meshData, logContent, subTaskEntity, resultUrl);
            } catch (InterruptedException e) {
                log.error(e.getMessage());
                // 恢复中断状态
                Thread.currentThread().interrupt();
                // 更新任务表状态为“异常”
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } catch (Exception e) {
                // 更新任务表状态为“异常”
                log.error(e.getMessage());
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskService.saveOrUpdate(subTaskEntity);
            } finally {
                subTaskEntity.setCmdLog(logContent.toString());
                subTaskService.saveOrUpdate(subTaskEntity);
                LoginUtil.removeAll();
            }
        }, executeScriptExecutorService);

        return ResponseResult.success();
    }

    public ResponseResult runOdbToVtu(String subTaskId, String localPath) {
//        SubTaskEntity byId = subTaskService.getById("7856396278c077a7a67d74b0d0e50dd2");
        SubTaskEntity byId = subTaskService.getById(subTaskId);
        /*
        TaskComputeResultEntity resultEntity = taskComputeResultService.getById(byId.getComputeResultId());
        if (resultEntity != null) {
            resultEntity.setVtuType(3);
            taskComputeResultService.saveOrUpdate(resultEntity);
        }
         */
        AssertUtil.isNull(byId, CodeEnum.SUBTASK_NOT_NULL);

        // 提取文件名，包括后缀
        String fileName = localPath.substring(localPath.lastIndexOf('\\') + 1);

        // 本地保存路径，包括文件名和后缀
        String destinationPath = odbToVtuPath + fileName;

        // 确保目标文件夹存在
        FileUtil.mkdir(new File(destinationPath).getParentFile());

        //结果文件复制到odb文件夹
        log.info("原obd文件路径:%s", localPath);
        log.info("现obd文件路径:%s", destinationPath);
        try {
            FileUtil.copy(localPath, destinationPath, false);
        } catch (Exception e) {
            log.error("复制失败");
        }


//        ProcessBuilder processBuilder1 = new ProcessBuilder("cmd.exe", "/c", jsonBuilder);

        StringBuilder logContent = new StringBuilder();

        List<String> vtuPaths = new ArrayList<>();

        CompletableFuture.runAsync(() -> {
            List<Path> paths = new ArrayList<>();
            try {
//              检查当前执行目录
                log.info(System.getProperty("user.dir"));
                Path scriptPath = ScriptExtractor.extractScript("python/odb2vtk.py");
                paths.add(scriptPath);
                paths.add(ScriptExtractor.extractScript("python/utilities.py"));
                paths.add(ScriptExtractor.extractScript("python/multiprocess.py"));
                String builder = "abaqus python " + scriptPath + " --header 0 " + byId.getTaskComputeResult().getVtu() +
                        " --odbFile " + destinationPath + " --outputFolder " + odbToVtuPath;

                log.info("===脚本 {}===", builder);
//              log.info("===转换json脚本 {}===", jsonBuilder);
                ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", builder);

                processBuilder.redirectErrorStream(true);
////                log.info(System.getProperty("user.dir"));
//
//                // 启动进程
                Process process = processBuilder.start();

                Future<?> outputFuture = getOdbToVtuOutPut(logContent, process, vtuPaths);


                process.waitFor();
                // Wait for the outputFuture to complete
                outputFuture.get();

                boolean matchesFormat = isLastLineExpectedFormat(logContent);
                if (matchesFormat) {
                    for (String vtuPath : vtuPaths) {
                        TaskResultVtuEntity taskResultVtuEntity = new TaskResultVtuEntity();
                        taskResultVtuEntity.setComputeResultId(byId.getTaskComputeResult().getId());
                        String vtuFileName = Paths.get(vtuPath).getFileName().toString();
                        taskResultVtuEntity.setVtuName(vtuFileName);
                        String s = upload2FileSystem(vtuPath, CodeEnum.TASK_VTU_UPLOAD_FAILED);
                        taskResultVtuEntity.setVtuUrl(s);
                        taskResultVtuService.save(taskResultVtuEntity);
                    }
                }
            } catch (InterruptedException e) {
                log.error(e.getMessage());
                // 恢复中断状态
                Thread.currentThread().interrupt();
            } catch (Exception e) {
                // 更新任务表状态为“异常”
                log.error(e.getMessage());
            } finally {
                for (Path path : paths) {
                    safelyDeleteScript(path);
                }
                // 删除临时目录
                safelyDeleteScript(ScriptExtractor.getTempDir());
            }
        }, runOdbToVtuCmdExecutorService);
        return ResponseResult.success();
    }
        // ~ Autowired
    // ========================================================================================================


}
